<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="kryo序列化原理：用过dubbo的开发人员，在选取序列化时都会根据“经验”来选kryo为序列化框，其原因是序列化协议非常高效，超过java原生序列化协议、hessian2协议，那kryo为什么高效呢？序列化协议，所谓的高效，通常应该从两方面考虑：  序列化后的二进制序列大小。（核心） 序列化、反序列化的速率。  本节将重点探讨，kryo在减少序列化化二进制流上做的努力。  序列化：将各种数据类型">
<meta property="og:type" content="article">
<meta property="og:title" content="源码分析Dubbo序列化-源码分析kryo各数据类型的序列化编码机制">
<meta property="og:url" content="https://www.codingw.net/posts/caabd6c1.html">
<meta property="og:site_name" content="中间件兴趣圈">
<meta property="og:description" content="kryo序列化原理：用过dubbo的开发人员，在选取序列化时都会根据“经验”来选kryo为序列化框，其原因是序列化协议非常高效，超过java原生序列化协议、hessian2协议，那kryo为什么高效呢？序列化协议，所谓的高效，通常应该从两方面考虑：  序列化后的二进制序列大小。（核心） 序列化、反序列化的速率。  本节将重点探讨，kryo在减少序列化化二进制流上做的努力。  序列化：将各种数据类型">
<meta property="og:locale">
<meta property="og:image" content="https://img-blog.csdn.net/20180812195919737?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">
<meta property="og:image" content="https://img-blog.csdn.net/20180812200302841?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">
<meta property="og:image" content="https://img-blog.csdn.net/20180812201011880?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">
<meta property="article:published_time" content="2020-11-22T10:26:01.000Z">
<meta property="article:modified_time" content="2021-04-26T12:10:44.371Z">
<meta property="article:author" content="中间件兴趣圈">
<meta property="article:tag" content="中间件">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img-blog.csdn.net/20180812195919737?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://www.codingw.net/posts/caabd6c1.html"/>





  <title>源码分析Dubbo序列化-源码分析kryo各数据类型的序列化编码机制 | 中间件兴趣圈</title>
  








<meta name="generator" content="Hexo 5.4.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">中间件兴趣圈</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">微信搜『中间件兴趣圈』，回复『Java』获取200本优质电子书</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://www.codingw.net/posts/caabd6c1.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="中间件兴趣圈">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">源码分析Dubbo序列化-源码分析kryo各数据类型的序列化编码机制</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-22T18:26:01+08:00">
                2020-11-22
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/dubbo/" itemprop="url" rel="index">
                    <span itemprop="name">dubbo</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          
             <span id="/posts/caabd6c1.html" class="leancloud_visitors" data-flag-title="源码分析Dubbo序列化-源码分析kryo各数据类型的序列化编码机制">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">阅读次数&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          
            <span class="post-meta-divider">|</span>
            <span class="page-pv"><i class="fa fa-file-o"></i>
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>次
            </span>
          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <div id="vip-container"><p>kryo序列化原理：<br>用过dubbo的开发人员，在选取序列化时都会根据“经验”来选kryo为序列化框，其原因是序列化协议非常高效，超过java原生序列化协议、hessian2协议，那kryo为什么高效呢？<br><strong>序列化协议，所谓的高效，通常应该从两方面考虑：</strong></p>
<ul>
<li>序列化后的二进制序列大小。（核心）</li>
<li>序列化、反序列化的速率。</li>
</ul>
<p>本节将重点探讨，kryo在减少序列化化二进制流上做的努力。</p>
<ul>
<li>序列化：将各种数据类型（基本类型、包装类型、对象、数组、集合）等序列化为byte数组的过程。</li>
<li>反序列化：将byte数组转换为各种数据类型（基本类型、包装类型、对象、数组、集合）。</li>
</ul>
<p>java中定义的数据类型所对应的序列化器在Kryo的构造函数中构造，其代码截图：<br><img src="https://img-blog.csdn.net/20180812195919737?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p>
<p>接下来将详细介绍java常用的数据类型的序列化机制，即Kryo是如何编码二进制流。</p>
<span id="more"></span>

<h2 id="1、DefaultSerializers-IntSerializer-int类型序列化"><a href="#1、DefaultSerializers-IntSerializer-int类型序列化" class="headerlink" title="1、DefaultSerializers$IntSerializer int类型序列化"></a>1、DefaultSerializers$IntSerializer int类型序列化</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">static public class IntSerializer extends Serializer&lt;Integer&gt; &#123;</span><br><span class="line">	&#123;</span><br><span class="line">		setImmutable(true);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	public void write (Kryo kryo, Output output, Integer object) &#123;</span><br><span class="line">		output.writeInt(object, false);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	public Integer read (Kryo kryo, Input input, Class&lt;Integer&gt; type) &#123;</span><br><span class="line">		return input.readInt(false);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="1-1-Integer-—-gt-byte-序列化"><a href="#1-1-Integer-—-gt-byte-序列化" class="headerlink" title="1.1 Integer —&gt; byte[] (序列化)"></a>1.1 Integer —&gt; byte[] (序列化)</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Output#writeInt (int value, boolean optimizePositive) </span><br><span class="line">public int writeInt (int value, boolean optimizePositive) throws KryoException &#123; &#x2F;&#x2F; @1</span><br><span class="line">return writeVarInt(value, optimizePositive);  &#x2F;&#x2F; @2</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：boolean optimizePositive，是否优化绝对值。如果optimizePositive:false，则会对value进行移位运算，如果是正数，则存放的值为原值的两倍，如果是负数的话，存放的值为绝对值的两倍减去一，其算法为：value = (value &lt;&lt; 1) ^ (value &gt;&gt; 31)，在反序列化时，通过该算法恢复原值：((result &gt;&gt;&gt; 1) ^ -(result &amp; 1))。</p>
<p>代码@2：调用writeVarInt，采用变长编码来存储int而不是固定4字节。<br>Output#writeVarInt</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">public int writeVarInt (int value, boolean optimizePositive) throws KryoException &#123;</span><br><span class="line">		if (!optimizePositive) value &#x3D; (value &lt;&lt; 1) ^ (value &gt;&gt; 31);</span><br><span class="line">		if (value &gt;&gt;&gt; 7 &#x3D;&#x3D; 0) &#123;                                           &#x2F;&#x2F; @1 </span><br><span class="line">			require(1);                                                    </span><br><span class="line">			buffer[position++] &#x3D; (byte)value;                  </span><br><span class="line">			return 1;</span><br><span class="line">		&#125;</span><br><span class="line">		if (value &gt;&gt;&gt; 14 &#x3D;&#x3D; 0) &#123;                                          &#x2F;&#x2F; @2</span><br><span class="line">			require(2);</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &amp; 0x7F) | 0x80);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 7);</span><br><span class="line">			return 2;</span><br><span class="line">		&#125;</span><br><span class="line">		if (value &gt;&gt;&gt; 21 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">			require(3);</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &amp; 0x7F) | 0x80);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 7 | 0x80);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 14);</span><br><span class="line">			return 3;</span><br><span class="line">		&#125;</span><br><span class="line">		if (value &gt;&gt;&gt; 28 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">			require(4);</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &amp; 0x7F) | 0x80);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 7 | 0x80);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 14 | 0x80);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 21);</span><br><span class="line">			return 4;</span><br><span class="line">		&#125;</span><br><span class="line">		require(5);</span><br><span class="line">		buffer[position++] &#x3D; (byte)((value &amp; 0x7F) | 0x80);</span><br><span class="line">		buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 7 | 0x80);</span><br><span class="line">		buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 14 | 0x80);</span><br><span class="line">		buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 21 | 0x80);</span><br><span class="line">		buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 28);</span><br><span class="line">		return 5;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>其思想是采取变长字节来存储int类型的数据，int在java是固定4字节，由于在应用中，一般使用的int数据都不会很大，4个字节中，存在高位字节全是存储0的情况，故kryo为了减少在序列化流中的大小，尽量按需分配，kryo采用1-5个字节来存储int数据，为什么int类型在JAVA中最多4个字节，为什么变长int可能需要5个字节才能存储呢？这与变长字节需要标志位有关，下文根据代码来推测kryo关于int序列化byte数组的编码规则。<br>代码@1：value &gt;&gt;&gt; 7 == 0 ，一个数字，无符号右移(高位补0)7位后为0，说明该数字只占一个字节，并且高两位必须为0，也就是该数字的范围在0-127（2^7 -1）,对于字节的高位，低位的说明如下：<br><img src="https://img-blog.csdn.net/20180812200302841?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br>如果该值范围为0-127直接，则使用1个字节存储int即可。在操作缓存区时buffer[position++] = (byte)value，需要向Output的缓存区申请1个字节的空间，然后进行赋值，并返回本次申请的存储空间，对于require方法在Byte[]、String序列化时重点讲解，包含缓存区的扩容，Output与输出流结合使用时的相关机制。</p>
<p>代码@2：value &gt;&gt;&gt; 14 == 0，如果数字的范围在0到2^14-1范围之间，则需要两个字节存储，这里为什么是14，其主要原因是，对于一个字节中的8位，kryo需要将高位用来当标记位，用来 标识是否还需要读取下一个字节。1：表示需要，0：表示不需要，也就是一个数据的结束。在变长int存储过中,一个字节8位kryo可用来存储数字有效位为 7 位 。举例演示一下：<br>kryo两字节能存储的数据的特点是高字节中前两位为0，例如：<br>0011 1011     0 010 1001<br>其存储方式为<br>buffer[0] =  先存储最后字节的低7位，010 1001 ，然后第一位之前，加1，表示还需要申请第二个字节来存储。此时buffer[0] = 1010 1001<br>buffer[1] =  存储 011 1011 0(这个0是原第一个字节未存储的部分) ，此时buffer[1]的8位中的高位为0，表示存储结束。<br>下图展示了kryo用2个字节存储一个int类型的数据的示意图。<br><img src="https://img-blog.csdn.net/20180812201011880?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br>同理，用3个字节可以表示2^21 -1。<br>kryo使用变长字节(1-5)个字节来存储int类型（java中固定占4字节）。</p>
<h3 id="1-2-int反序列化（byte-—-gt-int）"><a href="#1-2-int反序列化（byte-—-gt-int）" class="headerlink" title="1.2 int反序列化（byte[] —&gt; int）"></a>1.2 int反序列化（byte[] —&gt; int）</h3><p>反序列化就是根据上述编码规则，将byte[]序列化为int数字。<br>buffer[0] = 低位，，buffer[1] 高位,具体解码实现为：<br>Input#readVarInt</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;** Reads a 1-5 byte int. It is guaranteed that a varible length encoding will be used. *&#x2F;</span><br><span class="line">	public int readVarInt (boolean optimizePositive) throws KryoException &#123;</span><br><span class="line">		if (require(1) &lt; 5) return readInt_slow(optimizePositive);</span><br><span class="line">		int b &#x3D; buffer[position++];</span><br><span class="line">		int result &#x3D; b &amp; 0x7F;</span><br><span class="line">		if ((b &amp; 0x80) !&#x3D; 0) &#123;</span><br><span class="line">			byte[] buffer &#x3D; this.buffer;</span><br><span class="line">			b &#x3D; buffer[position++];</span><br><span class="line">			result |&#x3D; (b &amp; 0x7F) &lt;&lt; 7;</span><br><span class="line">			if ((b &amp; 0x80) !&#x3D; 0) &#123;</span><br><span class="line">				b &#x3D; buffer[position++];</span><br><span class="line">				result |&#x3D; (b &amp; 0x7F) &lt;&lt; 14;</span><br><span class="line">				if ((b &amp; 0x80) !&#x3D; 0) &#123;</span><br><span class="line">					b &#x3D; buffer[position++];</span><br><span class="line">					result |&#x3D; (b &amp; 0x7F) &lt;&lt; 21;</span><br><span class="line">					if ((b &amp; 0x80) !&#x3D; 0) &#123;</span><br><span class="line">						b &#x3D; buffer[position++];</span><br><span class="line">						result |&#x3D; (b &amp; 0x7F) &lt;&lt; 28;</span><br><span class="line">					&#125;</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		return optimizePositive ? result : ((result &gt;&gt;&gt; 1) ^ -(result &amp; 1));</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>Input#require(count)返回的是缓存区剩余字节数（可读）。其实现思路是，一个一个字节的读取，读到第一个字节后，首先提取有效存储位的数据，buffer[ 0 ] &amp; 0x7F，然后判断高位是否为1，如果不为1，直接返回，如果为1，则继续读取第二位buffer[1],同样首先提取有效数据位（低7位），然后对这数据向左移7位，在与buffer[0] 进行或运算。也就是，varint的存放是小端序列，越先读到的位，在整个int序列中越靠近低位。<br>2、String序列化，其实现类DefaultSerializers$StringSerializer。 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">static public class StringSerializer extends Serializer&lt;String&gt; &#123;</span><br><span class="line">	&#123;</span><br><span class="line">		setImmutable(true);</span><br><span class="line">		setAcceptsNull(true);      &#x2F;&#x2F; @1</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	public void write (Kryo kryo, Output output, String object) &#123;</span><br><span class="line">		output.writeString(object);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	public String read (Kryo kryo, Input input, Class&lt;String&gt; type) &#123;</span><br><span class="line">		return input.readString();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;      </span><br></pre></td></tr></table></figure>
<p>代码@1：String位不可变、允许为空，也就是序列化时需要考虑String s = null的情况。</p>
<h3 id="2-1-序列化-（String-—-gt-byte-）"><a href="#2-1-序列化-（String-—-gt-byte-）" class="headerlink" title="2.1 序列化 （String —-&gt; byte[]）"></a>2.1 序列化 （String —-&gt; byte[]）</h3><p>Output#writeString </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">public void writeString (String value) throws KryoException &#123;</span><br><span class="line">		if (value &#x3D;&#x3D; null) &#123;                                                                                &#x2F;&#x2F; @1</span><br><span class="line">			writeByte(0x80); &#x2F;&#x2F; 0 means null, bit 8 means UTF8.</span><br><span class="line">			return;</span><br><span class="line">		&#125;</span><br><span class="line">		int charCount &#x3D; value.length();      </span><br><span class="line">		if (charCount &#x3D;&#x3D; 0) &#123;    &#x2F;&#x2F; @2</span><br><span class="line">			writeByte(1 | 0x80); &#x2F;&#x2F; 1 means empty string, bit 8 means UTF8.</span><br><span class="line">			return;</span><br><span class="line">		&#125;</span><br><span class="line">		&#x2F;&#x2F; Detect ASCII.</span><br><span class="line">		boolean ascii &#x3D; false;</span><br><span class="line">		if (charCount &gt; 1 &amp;&amp; charCount &lt; 64) &#123;  &#x2F;&#x2F; @3</span><br><span class="line">			ascii &#x3D; true;</span><br><span class="line">			for (int i &#x3D; 0; i &lt; charCount; i++) &#123;</span><br><span class="line">				int c &#x3D; value.charAt(i);</span><br><span class="line">				if (c &gt; 127) &#123;</span><br><span class="line">					ascii &#x3D; false;</span><br><span class="line">					break;</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		if (ascii) &#123;     &#x2F;&#x2F; @4</span><br><span class="line">			if (capacity - position &lt; charCount)</span><br><span class="line">				writeAscii_slow(value, charCount);</span><br><span class="line">			else &#123;</span><br><span class="line">				value.getBytes(0, charCount, buffer, position);</span><br><span class="line">				position +&#x3D; charCount;</span><br><span class="line">			&#125;</span><br><span class="line">			buffer[position - 1] |&#x3D; 0x80;</span><br><span class="line">		&#125; else &#123;</span><br><span class="line">			writeUtf8Length(charCount + 1);      &#x2F;&#x2F; @5</span><br><span class="line">			int charIndex &#x3D; 0;</span><br><span class="line">			if (capacity - position &gt;&#x3D; charCount) &#123;     &#x2F;&#x2F; @6</span><br><span class="line">				&#x2F;&#x2F; Try to write 8 bit chars.</span><br><span class="line">				byte[] buffer &#x3D; this.buffer;</span><br><span class="line">				int position &#x3D; this.position;</span><br><span class="line">				for (; charIndex &lt; charCount; charIndex++) &#123;</span><br><span class="line">					int c &#x3D; value.charAt(charIndex);</span><br><span class="line">					if (c &gt; 127) break;</span><br><span class="line">					buffer[position++] &#x3D; (byte)c;</span><br><span class="line">				&#125;</span><br><span class="line">				this.position &#x3D; position;</span><br><span class="line">			&#125;</span><br><span class="line">			if (charIndex &lt; charCount) writeString_slow(value, charCount, charIndex);    &#x2F;&#x2F; @7</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>首先对字符串编码成字节序列，通常采用的编码方式为length:具体内容，通常的做法，表示字符串序列长度为固定字节，例如4位，那kryo是如何来表示的呢？请看下文分析。<br>代码@1：如果字符串为null,采用一个字节来表示长度，长度为0，并且该字节的高位填充1，表示字符串使用UTF-8编码，null字符串的最终表示为：1000 0000。<br>代码@2：空字符串表示，长度用1来表示，同样高位使用1填充表示字符串使用UTF-8编码，空字符串最终表示为：1000 0001。注：长度为1表示空字符串。<br>代码@3：如果字符长度大于1并且小于64，依次检查字符，如果其ascii小于127，则认为可以用ascii来表示单个字符，不能超过127的原因是，其中字节的高一位需要表示编码，0表示ascii,当用ascii编码来表示字符串是，第高2位需要用来表示是否结束标记。<br>代码@4：如果使用ascii编码，则单个字符，使用一个字节表示，高1位表示编码标记为，高2位表示是否结束标记。<br>代码@5：按照UTF-8编码，写入其长度，用变长int(varint)写入字符串长度，具体实现如下：<br>Output#writeUtf8Length</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">private void writeUtf8Length (int value) &#123;</span><br><span class="line">		if (value &gt;&gt;&gt; 6 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">			require(1);</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">		&#125; else if (value &gt;&gt;&gt; 13 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">			require(2);</span><br><span class="line">			byte[] buffer &#x3D; this.buffer;</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value | 0x40 | 0x80); &#x2F;&#x2F; Set bit 7 and 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 6);</span><br><span class="line">		&#125; else if (value &gt;&gt;&gt; 20 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">			require(3);</span><br><span class="line">			byte[] buffer &#x3D; this.buffer;</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value | 0x40 | 0x80); &#x2F;&#x2F; Set bit 7 and 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &gt;&gt;&gt; 6) | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 13);</span><br><span class="line">		&#125; else if (value &gt;&gt;&gt; 27 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">			require(4);</span><br><span class="line">			byte[] buffer &#x3D; this.buffer;</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value | 0x40 | 0x80); &#x2F;&#x2F; Set bit 7 and 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &gt;&gt;&gt; 6) | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &gt;&gt;&gt; 13) | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 20);</span><br><span class="line">		&#125; else &#123;</span><br><span class="line">			require(5);</span><br><span class="line">			byte[] buffer &#x3D; this.buffer;</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value | 0x40 | 0x80); &#x2F;&#x2F; Set bit 7 and 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &gt;&gt;&gt; 6) | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &gt;&gt;&gt; 13) | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)((value &gt;&gt;&gt; 20) | 0x80); &#x2F;&#x2F; Set bit 8.</span><br><span class="line">			buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 27);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>用来表示字符串长度的编码规则（int），第8位（高位）表示字符串的编码，第7位（高位）表示是否还需要读取下一个字节，也就是结束标记，1表示未结束，0表示结束。一个字节共8位，只有低6位用来存放数据， varint采取的是小端序列。<br>代码@6：如果当前缓存区有足够的空间，先尝试将字符串中单字节数据写入到buffer中，碰到第一个非单字节字符时，结束。<br>代码@7：将剩余空间写入缓存区，其实现方法：Output#writeString_slow(value, charCount, charIndex)<br>Output#writeString_slow</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">private void writeString_slow (CharSequence value, int charCount, int charIndex) &#123;</span><br><span class="line">		for (; charIndex &lt; charCount; charIndex++) &#123;                                                                            &#x2F;&#x2F; @1</span><br><span class="line">			if (position &#x3D;&#x3D; capacity) require(Math.min(, charCount - charIndex));                               &#x2F;&#x2F; @2</span><br><span class="line">			int c &#x3D; value.charAt(charIndex);                                                                                        &#x2F;&#x2F; @3</span><br><span class="line">			if (c &lt;&#x3D; 0x007F) &#123;                                                                                                               &#x2F;&#x2F; @4</span><br><span class="line">				buffer[position++] &#x3D; (byte)c;</span><br><span class="line">			&#125; else if (c &gt; 0x07FF) &#123;                                                                                                       &#x2F;&#x2F; @5</span><br><span class="line">				buffer[position++] &#x3D; (byte)(0xE0 | c &gt;&gt; 12 &amp; 0x0F);</span><br><span class="line">				require(2);</span><br><span class="line">				buffer[position++] &#x3D; (byte)(0x80 | c &gt;&gt; 6 &amp; 0x3F);</span><br><span class="line">				buffer[position++] &#x3D; (byte)(0x80 | c &amp; 0x3F);</span><br><span class="line">			&#125; else &#123;                                                                                                                                &#x2F;&#x2F; @6</span><br><span class="line">				buffer[position++] &#x3D; (byte)(0xC0 | c &gt;&gt; 6 &amp; 0x1F);</span><br><span class="line">				require(1);</span><br><span class="line">				buffer[position++] &#x3D; (byte)(0x80 | c &amp; 0x3F);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：循环遍历字符的字符。<br>代码@2：如果当前缓存区已经写满，尝试申请（capacity 与 charCount - charIndex ）的最小值，这里无需担心字符不是单字节申请charCount - charIndex空间不足的问题，后面我们会详细分析require方法，字节不够时会触发缓存区扩容或刷写到流中，再重复利用缓存区。<br>代码@3：int c = value.charAt(charIndex); 将字符类型转换为int类型，一个中文字符对应一个 int数字，这是因为java使用unicode编码，每个字符占用2个字节，char向int类型转换，就是将2字节的字节编码，转换成对应的二进制，然后用10进制表示的数字。<br>代码@4：如果值小于等0x7F(127)，直接存储在1个字节中，此时高位4个字节的范围在（0-7）<br>代码@5：如果值大于0x07FF(二进制 0000 0111 1111 1111)，第一个大于0x7F的值为(0000 1000 0000 0000),即2^12，数据有效位至少12位，使用3字节来存储，具体存储方式为：</p>
<ul>
<li>buffer[0] ：buffer[position++] = (byte)(0xE0 | c &gt;&gt; 12 &amp; 0x0F);  首先将c右移12位再与0x0F进行与操作，其意义就是先提取c的第16-13（4位的值），并与0xE0取或，最终的值为  0xE (16-13)位的值，从Input读取字符串可以看出，是根据0xE0作为存储该字符需要3个字节的依据，并且只取16-13位的值作为其高位的有效位，也就是说字符编码的值，不会超过0XFFFF,也就是两个字节(正好与java unicode编码吻合)。</li>
<li>buffer[1]：存储第12-7(共6位)，c &gt;&gt; 6 &amp; 0x3F，然后与0X80进行或，高位设置为1，表示UTF-8编码，其实再反序列化时，这个高位设置为1，未有实际作用。</li>
<li>buffer[2]：存储第6-1(共6位)，0x80 | c &amp; 0x3F，同样高位置1。</li>
</ul>
<h3 id="2-2-字符串反序列化-（byte-—-gt-String）"><a href="#2-2-字符串反序列化-（byte-—-gt-String）" class="headerlink" title="2.2 字符串反序列化 （byte[] —-&gt; String）"></a>2.2 字符串反序列化 （byte[] —-&gt; String）</h3><p>在讲解反序列化时，总结一下String序列化的编码规则</p>
<p>String序列化规则：String序列化的整体结构为  length + 内容,注意，这里的length不是内容字节的长度，而是String字符的长度。</p>
<ul>
<li><p>如果是null,则用1个字节表示，其二进制为 1000 0000。</p>
</li>
<li><p>如果是””空字符串，则用1个字节表示，其二进制为1000 0001。</p>
</li>
<li><p>如果字符长度大于1·且小于64，并且字符全是ascii字符（小等于127），则每个字符用一个字节表示，最后一个字节的高位置1，表示String字符的结束。【优化点，如果是ascii字符，编码时不需要使用length+内容的方式，而是直接写入内容】</p>
</li>
<li><p>如果不满足上述条件，则需要使用length + 内容的方式。</p>
<ul>
<li><p>用一个变长int写入字符的长度，每一字节，高两位分别为 编码标记（1:utf8）、是否结束标记（1:否;0:结束）</p>
</li>
<li><p>将内容用utf-8编码写入字节序列中，utf8，用变长字节（1-3）个字节表示一个字符（英文、中文）。每一个字节，使用6为，高两位为标志位。【16位】<br>3字节的存储为   【4位】 + 【6位】 + 【6位】，根据第一个字节高4位判断得出 需要几个字节来存储一个字符。<br>其反序列化的入口为Input#readString，就是按照上述规则进行解析即可，就不深入探讨了，有兴趣的话，可以自己去指定地方查阅。</p>
</li>
<li><p>boolean类型序列化：DefaultSerializers$BooleanSerializer<br>序列化：使用1个字节存储boolean类型，如果为true，则写入1，否则写入0。</p>
</li>
<li><p>byte类型序列化：DefaultSerializers$ByteSerializer<br>序列化：直接将byte写入字节流中即可。</p>
</li>
</ul>
</li>
</ul>
<h2 id="5、char类型序列化：DefaultSerializers-CharSerializer"><a href="#5、char类型序列化：DefaultSerializers-CharSerializer" class="headerlink" title="5、char类型序列化：DefaultSerializers$CharSerializer"></a>5、char类型序列化：DefaultSerializers$CharSerializer</h2><p>Output#writeChar</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;** Writes a 2 byte char. Uses BIG_ENDIAN byte order. *&#x2F;</span><br><span class="line">	public void writeChar (char value) throws KryoException &#123;</span><br><span class="line">		require(2);</span><br><span class="line">		buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 8);</span><br><span class="line">		buffer[position++] &#x3D; (byte)value;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>序列化：char在java中使用2字节存储（unicode),kryo在序列化时，按大端字节的顺序，将char写入字节流</p>
<h2 id="6、short类型序列化：DefaultSerializers-ShortSerializer"><a href="#6、short类型序列化：DefaultSerializers-ShortSerializer" class="headerlink" title="6、short类型序列化：DefaultSerializers$ShortSerializer"></a>6、short类型序列化：DefaultSerializers$ShortSerializer</h2><p>Output#writeShort</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;** Writes a 2 byte short. Uses BIG_ENDIAN byte order. *&#x2F;</span><br><span class="line">	public void writeShort (int value) throws KryoException &#123;</span><br><span class="line">		require(2);</span><br><span class="line">		buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 8);</span><br><span class="line">		buffer[position++] &#x3D; (byte)value;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>序列化：与char类型序列化一样，采用大端字节顺序存储。</p>
<h2 id="7、long类型序列化：DefaultSerializers-LongSerializer"><a href="#7、long类型序列化：DefaultSerializers-LongSerializer" class="headerlink" title="7、long类型序列化：DefaultSerializers$LongSerializer"></a>7、long类型序列化：DefaultSerializers$LongSerializer</h2><p>Output#writeLong</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">public int writeLong (long value, boolean optimizePositive) throws KryoException &#123;</span><br><span class="line">		return writeVarLong(value, optimizePositive);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>序列化：采取变长字节（1-9）位来存储long，其编码规则与int变长类型一致，每个字节的高位用来表示是否结束，1：表示还需要继续读取下一个字节，0：表示结束。</p>
<h2 id="8、float类型序列化：DefaultSerializers-FloatSerializer"><a href="#8、float类型序列化：DefaultSerializers-FloatSerializer" class="headerlink" title="8、float类型序列化：DefaultSerializers$FloatSerializer"></a>8、float类型序列化：DefaultSerializers$FloatSerializer</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;** Writes a 4 byte float. *&#x2F;</span><br><span class="line">public void writeFloat (float value) throws KryoException &#123;</span><br><span class="line">	writeInt(Float.floatToIntBits(value));</span><br><span class="line">&#125;</span><br><span class="line">&#x2F;** Writes a 4 byte int. Uses BIG_ENDIAN byte order. *&#x2F;</span><br><span class="line">public void writeInt (int value) throws KryoException &#123;</span><br><span class="line">	require(4);</span><br><span class="line">	byte[] buffer &#x3D; this.buffer;</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt; 24);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt; 16);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt; 8);</span><br><span class="line">	buffer[position++] &#x3D; (byte)value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>序列化：首先将float按照IEEE 754编码标准，转换为int类型，然后按大端序列，使用固定长度4字节来存储float，这里之所以不使用变长字节来存储float，是因为，使用Float.floatToIntBits(value)产生的值，比较大，基本都需要使用4字才能存储，如果使用变长字节，则需要5字节，反而消耗的存储空间更大。</p>
<h2 id="9、DefaultSerializers-DoubleSerializer"><a href="#9、DefaultSerializers-DoubleSerializer" class="headerlink" title="9、DefaultSerializers$DoubleSerializer"></a>9、DefaultSerializers$DoubleSerializer</h2><p>Output#writeDouble序列化：首先将Double按照IEEE 754编码标准转换为Long，然后才去固定8字节存储。<br>到目前为止，介绍了8种基本类型(boolean、byte、char、short、int、float、long、double)和String类型的序列化与反序列化。</p>
<h2 id="10、BigInteger序列化：DefaultSerializers-BigIntegerSerializer"><a href="#10、BigInteger序列化：DefaultSerializers-BigIntegerSerializer" class="headerlink" title="10、BigInteger序列化：DefaultSerializers$BigIntegerSerializer"></a>10、BigInteger序列化：DefaultSerializers$BigIntegerSerializer</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;** Writes an 8 byte double. *&#x2F;</span><br><span class="line">public void writeDouble (double value) throws KryoException &#123;</span><br><span class="line">	writeLong(Double.doubleToLongBits(value));</span><br><span class="line">&#125;</span><br><span class="line">&#x2F;** Writes an 8 byte long. Uses BIG_ENDIAN byte order. *&#x2F;</span><br><span class="line">public void writeLong (long value) throws KryoException &#123;</span><br><span class="line">	require(8);</span><br><span class="line">	byte[] buffer &#x3D; this.buffer;</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 56);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 48);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 40);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 32);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 24);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 16);</span><br><span class="line">	buffer[position++] &#x3D; (byte)(value &gt;&gt;&gt; 8);</span><br><span class="line">	buffer[position++] &#x3D; (byte)value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>BigInteger序列化实现,整体格式与String类型一样，由length + 内容构成。<br>1、如果为null，则写入一个字节，其值为0，表示长度为0。<br>2、如果为BigInteger.ZERO,则长度写入2，随后再写入1个字节的内容，字节内容为0，表示ZERO。<br>3、将BigInteger转换成byte[]数组，首先写入长度=（byte数组长度 + 1）,然后写入byte数组的内容即可。</p>
<h2 id="11、BigDecimal序列化：DefaultSerializers-BigDecimalSerializer"><a href="#11、BigDecimal序列化：DefaultSerializers-BigDecimalSerializer" class="headerlink" title="11、BigDecimal序列化：DefaultSerializers$BigDecimalSerializer"></a>11、BigDecimal序列化：DefaultSerializers$BigDecimalSerializer</h2><p>BigDecimal的序列化与BigInteger一样，首先是通过BigDecimal#unscaledValue方法返回对应的BigInteger,然后序列化，在反序列化时通过BigInteger创建对应的BigDecimal即可。</p>
<h2 id="12、Class实例序列化：DefaultSerializers-ClassSerializer"><a href="#12、Class实例序列化：DefaultSerializers-ClassSerializer" class="headerlink" title="12、Class实例序列化：DefaultSerializers$ClassSerializer"></a>12、Class实例序列化：DefaultSerializers$ClassSerializer</h2><p>DefaultSerializers$ClassSerializer#write</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public void write (Kryo kryo, Output output, Class object) &#123;</span><br><span class="line">	kryo.writeClass(output, object); &#x2F;&#x2F; @1</span><br><span class="line">	output.writeByte((object !&#x3D; null &amp;&amp; object.isPrimitive()) ? 1 : 0); &#x2F;&#x2F; @2</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：调用Kryo的writeClass方法序列化Class实例。<br>代码@2：写入是否是包装类型（针对8种基本类型）。<br>接下来我们重点分析Kryo#writeClass</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public Registration writeClass (Output output, Class type) &#123;</span><br><span class="line">   if (output &#x3D;&#x3D; null) throw new IllegalArgumentException(&quot;output cannot be null.&quot;);</span><br><span class="line">   try &#123;</span><br><span class="line">       return classResolver.writeClass(output, type);    &#x2F;&#x2F; @1</span><br><span class="line">   &#125; finally &#123;</span><br><span class="line">      if (depth &#x3D;&#x3D; 0 &amp;&amp; autoReset) reset();    &#x2F;&#x2F; @2</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：首先调用ClassResolver.wreteClass方法。<br>代码@2：完成一次写入后，需要重置Kryo中的临时数据结构，这也就是kryo实例非线程安全的原因，其中几个重要的数据结构会再ClassResolver.writeClass中详细说明。<br>DefaultClassResolver#writeClass </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public Registration writeClass (Output output, Class type) &#123;</span><br><span class="line">    if (type &#x3D;&#x3D; null) &#123;   &#x2F;&#x2F; @1</span><br><span class="line">	if (TRACE || (DEBUG &amp;&amp; kryo.getDepth() &#x3D;&#x3D; 1))</span><br><span class="line">            log(&quot;Write&quot;, null);</span><br><span class="line">	output.writeVarInt(Kryo.NULL, true);</span><br><span class="line">  return null;</span><br><span class="line">    &#125;</span><br><span class="line">    Registration registration &#x3D; kryo.getRegistration(type);     &#x2F;&#x2F; @2</span><br><span class="line">    if (registration.getId() &#x3D;&#x3D; NAME)                                      &#x2F;&#x2F; @3</span><br><span class="line">writeName(output, type, registration);</span><br><span class="line">    else &#123;</span><br><span class="line">		if (TRACE) </span><br><span class="line">trace(&quot;kryo&quot;, &quot;Write class &quot; + registration.getId() + &quot;: &quot; + className(type));</span><br><span class="line">		output.writeVarInt(registration.getId() + 2, true);    &#x2F;&#x2F; @4</span><br><span class="line">   &#125;</span><br><span class="line">   return registration;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：如果type为null，则存储Kryo.NULL(0)，使用变长int来存储，0在变长int中占用1个字节。<br>代码@2：根据type从kryo获取类注册信息，如果有调用kryo#public Registration register (Class type)方法，则会返回其注册关系。<br>代码@3：如果不存在注册关系，则需要将类型的全名写入。<br>代码@4：如果存在注册关系，则registration.getId()将不等于Kryo.NAME(-1)，则将(registration.getId() + 2)使用变长int写入字节流即可。<br>从这里看出，如果将类预先注册到kryo中，序列化字节流将变的更小，所谓的kryo类注册机制就是将字符串的类全路径名替换为数字，但数字的分配与注册顺序相关，所有，如果要使用类注册机制，必须在kryo对象创建时首先注册，确保注册顺序一致。<br>解析来重点分析一下writeName方法<br>DefaultClassResolver#writeName</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">protected void writeName (Output output, Class type, Registration registration) &#123;</span><br><span class="line">	output.writeVarInt(NAME + 2, true);      &#x2F;&#x2F; @1</span><br><span class="line">	if (classToNameId !&#x3D; null) &#123;      &#x2F;&#x2F; @2</span><br><span class="line">		int nameId &#x3D; classToNameId.get(type, -1);    &#x2F;</span><br><span class="line">		if (nameId !&#x3D; -1) &#123;   &#x2F;&#x2F;</span><br><span class="line">			if (TRACE) trace(&quot;kryo&quot;, &quot;Write class name reference &quot; + nameId + &quot;: &quot; + className(type));</span><br><span class="line">			output.writeVarInt(nameId, true);</span><br><span class="line">			return;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#x2F;&#x2F; Only write the class name the first time encountered in object graph.</span><br><span class="line">	if (TRACE) trace(&quot;kryo&quot;, &quot;Write class name: &quot; + className(type));</span><br><span class="line">	int nameId &#x3D; nextNameId++;    &#x2F;&#x2F; @3</span><br><span class="line">	if (classToNameId &#x3D;&#x3D; null) classToNameId &#x3D; new IdentityObjectIntMap();    &#x2F;&#x2F; @4</span><br><span class="line">	classToNameId.put(type, nameId);     &#x2F;&#x2F; @5</span><br><span class="line">	output.writeVarInt(nameId, true);        &#x2F;&#x2F; @6</span><br><span class="line">	output.writeString(type.getName());   &#x2F;&#x2F; @7</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：由于是要写入类的全路径名，故首先使用变长int编码写入一个标记，表示是存储的类名，而不是一个ID。其标志位为 NAME+2 = 1。，存储0表示null。<br>代码@2：如果classToNameId 不为空（IdentityObjectIntMap&lt; Class&gt;），根据type获取nameId，如果不为空并且从缓存中能获取到nameId，则直接写入nameId,而不是写入类名，这里指在一次序列化过程中，同一个类名例如（cn.uce.test.Test）只写入一次，其他级联（重复）出现时，为其分配一个ID，进行缓存，具体可以从下面的代码中得知其意图。<br>代码@3：首先分配一全局递增的nameId。<br>代码@4：如果classToNameId 为空，则创建一个实例。<br>代码@5：将type与nameId进行缓存。<br>代码@6：写入nameId。<br>代码@7：写入type的全路径名。<br>注意Kryo#writeClass ,一次序列化Class实例后会调用reset方法，最终会清除本次classToNameId ,classToNameId并不能做一个全据的缓存的主要原因是，在不同的JVM虚拟机中，同一个class type对应的nameId不一定相同，故无法实现共存，只能是作为一个优化，在一次类序列化中，如果存在同一个类型，则第一个写入类全路径名，后面出现的则使用id(int)来存储，节省空间。<br>为了加深上述理解，我们再来看一下Class实例的反序列化：<br>DefaultClassResolver#readClass</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public Registration readClass (Input input) &#123;</span><br><span class="line">	int classID &#x3D; input.readVarInt(true);    &#x2F;&#x2F; @1</span><br><span class="line">	switch (classID) &#123;</span><br><span class="line">	case Kryo.NULL:                                 &#x2F;&#x2F; @2</span><br><span class="line">		if (TRACE || (DEBUG &amp;&amp; kryo.getDepth() &#x3D;&#x3D; 1)) log(&quot;Read&quot;, null);</span><br><span class="line">		return null;</span><br><span class="line">	case NAME + 2: &#x2F;&#x2F; Offset for NAME and NULL.      &#x2F;&#x2F; @3</span><br><span class="line">		return readName(input);</span><br><span class="line">	&#125;</span><br><span class="line">	if (classID &#x3D;&#x3D; memoizedClassId) return memoizedClassIdValue;</span><br><span class="line">	Registration registration &#x3D; idToRegistration.get(classID - 2);  </span><br><span class="line">	if (registration &#x3D;&#x3D; null) throw new KryoException(&quot;Encountered unregistered class ID: &quot; + (classID - 2));</span><br><span class="line">  if (TRACE) trace(&quot;kryo&quot;, &quot;Read class &quot; + (classID - 2) + &quot;: &quot; + className(registration.getType()));</span><br><span class="line">	memoizedClassId &#x3D; classID;</span><br><span class="line">	memoizedClassIdValue &#x3D; registration;</span><br><span class="line">	return registration;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：首先读取一个变长int。<br>代码@2：如果为Kryo.NULL表示为null，直接返回null即可。<br>代码@3：如果为NAME + 2则表示为存储的是类的全路径名，则调用readName解析类的名字。<br>代码@4：如果不为上述值，说明存储的是类型对应的ID值，也就是使用了类注册机制。 之所以idToRegistration.get(classID - 2)，是因为在存储时就是nameId + 2。因为，0（代表null），1：代表按类全路径名存储，nameId是从3开始存储。<br>接下来再重点看一下readName的实现：<br>DefaultClassResolver#readName</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">protected Registration readName (Input input) &#123;</span><br><span class="line">	int nameId &#x3D; input.readVarInt(true);   </span><br><span class="line">	if (nameIdToClass &#x3D;&#x3D; null) nameIdToClass &#x3D; new IntMap();</span><br><span class="line">	Class type &#x3D; nameIdToClass.get(nameId);    </span><br><span class="line">	if (type &#x3D;&#x3D; null) &#123;</span><br><span class="line">		&#x2F;&#x2F; Only read the class name the first time encountered in object graph.</span><br><span class="line">		String className &#x3D; input.readString();</span><br><span class="line">		type &#x3D; getTypeByName(className);</span><br><span class="line">		if (type &#x3D;&#x3D; null) &#123;</span><br><span class="line">			try &#123;</span><br><span class="line">				type &#x3D; Class.forName(className, false, kryo.getClassLoader());</span><br><span class="line">			&#125; catch (ClassNotFoundException ex) &#123;</span><br><span class="line">				if (WARN) warn(&quot;kryo&quot;, &quot;Unable to load class &quot; + className + &quot; with kryo&#39;s ClassLoader. Retrying with current..&quot;);</span><br><span class="line">				try &#123;</span><br><span class="line">					type &#x3D; Class.forName(className);</span><br><span class="line">				&#125; catch (ClassNotFoundException e) &#123;</span><br><span class="line">					throw new KryoException(&quot;Unable to find class: &quot; + className, ex);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">			if (nameToClass &#x3D;&#x3D; null) nameToClass &#x3D; new ObjectMap();</span><br><span class="line">			nameToClass.put(className, type);</span><br><span class="line">		   &#125;</span><br><span class="line">		  nameIdToClass.put(nameId, type);</span><br><span class="line">		 if (TRACE) trace(&quot;kryo&quot;, &quot;Read class name: &quot; + className);</span><br><span class="line">	&#125; else &#123;</span><br><span class="line">		if (TRACE) trace(&quot;kryo&quot;, &quot;Read class name reference &quot; + nameId + &quot;: &quot; + className(type));</span><br><span class="line">	&#125;</span><br><span class="line">	return kryo.getRegistration(type);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>首先读取类的id，因为在序列化类时，如果序列化字符串时，首先先用变长int存储类型的nameId，然后再序列化类的全路径名，这样在一次反序列化时，第一次序列化时，将全列的全路径使用Class.forName实例化对象后，然后存储在局部方法缓存中（IntMap）中，在这一次序列化时再碰到同类型时，则根据id则可以找到对象。</p>
<p>Class实例序列化总结：<br><strong>Class实例序列化需求：序列化类的全路径名，反序列化时根据Class.forName生成对应的实例。</strong></p>
<p><strong>kryo序列化Class实例的编码规则：</strong><br>1、如果为null，用变长int，实际使用1个字节，存储值为0。<br>2、如果该类通过类注册机制注册到kryo时，则序列化 (nameId + 2)，用变长int存储。<br>3、如果该类未通过类注册机制注册到kryo，在一次序列化过程中（包含级联）时，类型第一次出现时，会分配一个nameId，将nameId+type全路径序列化，后续再出现该类型，则只序列化nameId即可。</p>
<h2 id="13、DefaultSerializers-DateSerializer"><a href="#13、DefaultSerializers-DateSerializer" class="headerlink" title="13、DefaultSerializers$DateSerializer"></a>13、DefaultSerializers$DateSerializer</h2><p>java.Util.Date、java.sql.Date等序列化时，只需序列化Date#getTime()返回的long类型，反序列化时根据long类型创建对应的实例即可。long类型的编码使用变长long格式进行序列化。</p>
<h2 id="14、枚举类型Enum序列化DefaultSerializers-EnumSerializer"><a href="#14、枚举类型Enum序列化DefaultSerializers-EnumSerializer" class="headerlink" title="14、枚举类型Enum序列化DefaultSerializers$EnumSerializer"></a>14、枚举类型Enum序列化DefaultSerializers$EnumSerializer</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">static public class EnumSerializer extends Serializer&lt;Enum&gt; &#123;</span><br><span class="line">		&#123;</span><br><span class="line">			setImmutable(true);</span><br><span class="line">			setAcceptsNull(true);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		private Object[] enumConstants;</span><br><span class="line"></span><br><span class="line">		public EnumSerializer (Class&lt;? extends Enum&gt; type) &#123;</span><br><span class="line">			enumConstants &#x3D; type.getEnumConstants();</span><br><span class="line">			if (enumConstants &#x3D;&#x3D; null) throw new IllegalArgumentException(&quot;The type must be an enum: &quot; + type);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		public void write (Kryo kryo, Output output, Enum object) &#123;</span><br><span class="line">			if (object &#x3D;&#x3D; null) &#123;</span><br><span class="line">				output.writeVarInt(NULL, true);</span><br><span class="line">				return;</span><br><span class="line">			&#125;</span><br><span class="line">			output.writeVarInt(object.ordinal() + 1, true);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		public Enum read (Kryo kryo, Input input, Class&lt;Enum&gt; type) &#123;</span><br><span class="line">			int ordinal &#x3D; input.readVarInt(true);</span><br><span class="line">			if (ordinal &#x3D;&#x3D; NULL) return null;</span><br><span class="line">			ordinal--;</span><br><span class="line">			if (ordinal &lt; 0 || ordinal &gt; enumConstants.length - 1)</span><br><span class="line">				throw new KryoException(&quot;Invalid ordinal for enum \&quot;&quot; + type.getName() + &quot;\&quot;: &quot; + ordinal);</span><br><span class="line">			Object constant &#x3D; enumConstants[ordinal];</span><br><span class="line">			return (Enum)constant;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>枚举类型序列化（支持null）：<br>1、如果为null，则使用变长int，实际用一个字节存储0。<br>2、如果不为null，使用变长int，存储object.ordinal()+1,也就是序列化该值在枚举类型常量数组中的下标，由于0代表为空，则下标从1开始。<br>在反序列化时，通过Enum.class.getEnumConstants()获取枚举类型的常量数组，然后从二进制流中获取下标即可。</p>
<h2 id="15、EnumSet-类型序列化：DefaultSerializers-EnumSetSerializer"><a href="#15、EnumSet-类型序列化：DefaultSerializers-EnumSetSerializer" class="headerlink" title="15、EnumSet 类型序列化：DefaultSerializers$EnumSetSerializer"></a>15、EnumSet 类型序列化：DefaultSerializers$EnumSetSerializer</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">static public class EnumSetSerializer extends Serializer&lt;EnumSet&gt; &#123;</span><br><span class="line">	public void write (Kryo kryo, Output output, EnumSet object) &#123;</span><br><span class="line">		Serializer serializer;</span><br><span class="line">		if (object.isEmpty()) &#123;     &#x2F;&#x2F; @1</span><br><span class="line">			EnumSet tmp &#x3D; EnumSet.complementOf(object);    &#x2F;&#x2F; @2</span><br><span class="line">			if (tmp.isEmpty()) throw new KryoException(&quot;An EnumSet must have a defined Enum to be serialized.&quot;);</span><br><span class="line">			serializer &#x3D; kryo.writeClass(output, tmp.iterator().next().getClass()).getSerializer();    &#x2F;&#x2F; @3</span><br><span class="line">		&#125; else &#123;</span><br><span class="line">			serializer &#x3D; kryo.writeClass(output, object.iterator().next().getClass()).getSerializer();</span><br><span class="line">		&#125;</span><br><span class="line">		output.writeInt(object.size(), true);  &#x2F;&#x2F; @4</span><br><span class="line">		for (Object element : object)     &#x2F;&#x2F; @5</span><br><span class="line">			serializer.write(kryo, output, element);</span><br><span class="line">	  &#125;</span><br><span class="line"></span><br><span class="line">	public EnumSet read (Kryo kryo, Input input, Class&lt;EnumSet&gt; type) &#123;</span><br><span class="line">		Registration registration &#x3D; kryo.readClass(input);</span><br><span class="line">		EnumSet object &#x3D; EnumSet.noneOf(registration.getType());</span><br><span class="line">		Serializer serializer &#x3D; registration.getSerializer();</span><br><span class="line">		int length &#x3D; input.readInt(true);</span><br><span class="line">		for (int i &#x3D; 0; i &lt; length; i++)</span><br><span class="line">			object.add(serializer.read(kryo, input, null));</span><br><span class="line">		return object;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	public EnumSet copy (Kryo kryo, EnumSet original) &#123;</span><br><span class="line">		return EnumSet.copyOf(original);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>EnumSet 是一个专为枚举设计的集合类，EnumSet中的所有元素都必须是指定枚举类型的枚举值。在序列化EnumSet时，需要将EnumSet中存储的枚举类型进行序列化，然后再序列每一个枚举值。<br>序列化过程：<br>-<br>代码@1：如果序列化的EnumSet为空，则通过代码EnumSet.complementOf方法创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet集合，新EnumSet集合包含原EnumSet集合所不包含的、此类枚举类剩下的枚举值（即新EnumSet集合和原EnumSet集合的集合元素加起来是该枚举类的所有枚举值）。</p>
<p>代码@3：首先序列化EnumSet中的枚举类型Class实例，并获取枚举类型对应的序列器。</p>
<p>代码@4：序列化EnumSet中元素的个数。</p>
<p>代码@5：逐一序列化EnumSet中元素（一个个枚举值）。</p>
<h2 id="16、StringBuffer序列化DefaultSerializers-StringBufferSerializer"><a href="#16、StringBuffer序列化DefaultSerializers-StringBufferSerializer" class="headerlink" title="16、StringBuffer序列化DefaultSerializers$StringBufferSerializer"></a>16、StringBuffer序列化DefaultSerializers$StringBufferSerializer</h2><p>序列化：与String序列化一致。</p>
<h2 id="17、StringBuilder序列化DefaultSerializers-StringBuilderSerializer"><a href="#17、StringBuilder序列化DefaultSerializers-StringBuilderSerializer" class="headerlink" title="17、StringBuilder序列化DefaultSerializers$StringBuilderSerializer"></a>17、StringBuilder序列化DefaultSerializers$StringBuilderSerializer</h2><p>序列化：与String序列化一致。</p>
<h2 id="18、TreeMap序列化DefaultSerializers-TreeMapSerializer"><a href="#18、TreeMap序列化DefaultSerializers-TreeMapSerializer" class="headerlink" title="18、TreeMap序列化DefaultSerializers$TreeMapSerializer"></a>18、TreeMap序列化DefaultSerializers$TreeMapSerializer</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">static public class TreeMapSerializer extends MapSerializer &#123;</span><br><span class="line">	public void write (Kryo kryo, Output output, Map map) &#123;</span><br><span class="line">		TreeMap treeMap &#x3D; (TreeMap)map;</span><br><span class="line">		kryo.writeClassAndObject(output, treeMap.comparator());</span><br><span class="line">		super.write(kryo, output, map);</span><br><span class="line">	&#125;</span><br><span class="line">          &#x2F;&#x2F;  ...省略部分代码</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>TreeMap的序列，首先，先序列化TreeMap的比较器，然后再序列化TreeMap中的数据。<br>序列化数据请看MapSerializer<br>MapSerializer#write </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">public void write (Kryo kryo, Output output, Map map) &#123;</span><br><span class="line">		int length &#x3D; map.size();</span><br><span class="line">		output.writeInt(length, true);</span><br><span class="line"></span><br><span class="line">		Serializer keySerializer &#x3D; this.keySerializer;</span><br><span class="line">		if (keyGenericType !&#x3D; null) &#123;</span><br><span class="line">			if (keySerializer &#x3D;&#x3D; null) keySerializer &#x3D; kryo.getSerializer(keyGenericType);</span><br><span class="line">			keyGenericType &#x3D; null;</span><br><span class="line">		&#125;</span><br><span class="line">		Serializer valueSerializer &#x3D; this.valueSerializer;</span><br><span class="line">		if (valueGenericType !&#x3D; null) &#123;</span><br><span class="line">			if (valueSerializer &#x3D;&#x3D; null) valueSerializer &#x3D; kryo.getSerializer(valueGenericType);</span><br><span class="line">			valueGenericType &#x3D; null;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		for (Iterator iter &#x3D; map.entrySet().iterator(); iter.hasNext();) &#123;</span><br><span class="line">			Entry entry &#x3D; (Entry)iter.next();</span><br><span class="line">			if (keySerializer !&#x3D; null) &#123;</span><br><span class="line">				if (keysCanBeNull)</span><br><span class="line">					kryo.writeObjectOrNull(output, entry.getKey(), keySerializer);</span><br><span class="line">				else</span><br><span class="line">					kryo.writeObject(output, entry.getKey(), keySerializer);</span><br><span class="line">			&#125; else</span><br><span class="line">				kryo.writeClassAndObject(output, entry.getKey());</span><br><span class="line">			if (valueSerializer !&#x3D; null) &#123;</span><br><span class="line">				if (valuesCanBeNull)</span><br><span class="line">					kryo.writeObjectOrNull(output, entry.getValue(), valueSerializer);</span><br><span class="line">				else</span><br><span class="line">					kryo.writeObject(output, entry.getValue(), valueSerializer);</span><br><span class="line">			&#125; else</span><br><span class="line">				kryo.writeClassAndObject(output, entry.getValue());</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>其序列化方法就是遍历Map中的元素，调用Kryo#writeClassAndObject进行序列化，Kryo#writeClassAndObject涉及到Kryo整个序列化流程，将在下节介绍。</p>
<p>本节就讲述到这里了，，本节详细分析了Kryo对各种数据类型的序列化机制，其再降低序列化大小方面做了如下优化：</p>
<p>1、Kryo序列化的“对象”是数据以及少量元信息，这和JAVA默认的序列化的本质区别，java默认的序列化的目的是语言层面的，将类、对象的所有信息都序列化了，也就是就算是不加载class的定义，也能根据序列化后的信息动态构建类的所有信息。而Kryo反序列化时，必须能加载类的定义，这样Kryo能节省大量的字节空间。<br>2、使用变长int、变长long存储int、long类型，大大节省空间。<br>3、元数据（字符串类型）使用缓存机制，重复出现的字符串使用int来存储，节省存储空间。<br>4、字符串类型使用UTF-8存储，但会使用ascii码进一步优化空间。</p>
<p>下一节，将重点分析Kryo序列化的过程，其入口函数：Kryo#writeClassAndObject。</p>
</div>

			<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
			<script>
			var isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
			if (!isMobile) {
			    var btw = new BTWPlugin();
			    btw.init({
			        "id": "vip-container",
			        "blogId": "18019-1573088808868-542",
			        "name": "中间件兴趣圈",
			        "qrcode": "https://img-blog.csdnimg.cn/20190314214003962.jpg",
			        "keyword": "more"
			    });
			}
			</script>
		
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/posts/c1c80d83.html" rel="next" title="源码分析Dubbo序列化-源码分析kryo序列化实现原理">
                <i class="fa fa-chevron-left"></i> 源码分析Dubbo序列化-源码分析kryo序列化实现原理
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/posts/25a8fc09.html" rel="prev" title="源码分析Dubbo网络通讯篇之NettyServer网络事件之线程池">
                源码分析Dubbo网络通讯篇之NettyServer网络事件之线程池 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name"></p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">139</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">18</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#1%E3%80%81DefaultSerializers-IntSerializer-int%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96"><span class="nav-number">1.</span> <span class="nav-text">1、DefaultSerializers$IntSerializer int类型序列化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-Integer-%E2%80%94-gt-byte-%E5%BA%8F%E5%88%97%E5%8C%96"><span class="nav-number">1.1.</span> <span class="nav-text">1.1 Integer —&gt; byte[] (序列化)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-int%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%88byte-%E2%80%94-gt-int%EF%BC%89"><span class="nav-number">1.2.</span> <span class="nav-text">1.2 int反序列化（byte[] —&gt; int）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-%E5%BA%8F%E5%88%97%E5%8C%96-%EF%BC%88String-%E2%80%94-gt-byte-%EF%BC%89"><span class="nav-number">1.3.</span> <span class="nav-text">2.1 序列化 （String —-&gt; byte[]）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%EF%BC%88byte-%E2%80%94-gt-String%EF%BC%89"><span class="nav-number">1.4.</span> <span class="nav-text">2.2 字符串反序列化 （byte[] —-&gt; String）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5%E3%80%81char%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-CharSerializer"><span class="nav-number">2.</span> <span class="nav-text">5、char类型序列化：DefaultSerializers$CharSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6%E3%80%81short%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-ShortSerializer"><span class="nav-number">3.</span> <span class="nav-text">6、short类型序列化：DefaultSerializers$ShortSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7%E3%80%81long%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-LongSerializer"><span class="nav-number">4.</span> <span class="nav-text">7、long类型序列化：DefaultSerializers$LongSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8%E3%80%81float%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-FloatSerializer"><span class="nav-number">5.</span> <span class="nav-text">8、float类型序列化：DefaultSerializers$FloatSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9%E3%80%81DefaultSerializers-DoubleSerializer"><span class="nav-number">6.</span> <span class="nav-text">9、DefaultSerializers$DoubleSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10%E3%80%81BigInteger%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-BigIntegerSerializer"><span class="nav-number">7.</span> <span class="nav-text">10、BigInteger序列化：DefaultSerializers$BigIntegerSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11%E3%80%81BigDecimal%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-BigDecimalSerializer"><span class="nav-number">8.</span> <span class="nav-text">11、BigDecimal序列化：DefaultSerializers$BigDecimalSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12%E3%80%81Class%E5%AE%9E%E4%BE%8B%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-ClassSerializer"><span class="nav-number">9.</span> <span class="nav-text">12、Class实例序列化：DefaultSerializers$ClassSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13%E3%80%81DefaultSerializers-DateSerializer"><span class="nav-number">10.</span> <span class="nav-text">13、DefaultSerializers$DateSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14%E3%80%81%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8BEnum%E5%BA%8F%E5%88%97%E5%8C%96DefaultSerializers-EnumSerializer"><span class="nav-number">11.</span> <span class="nav-text">14、枚举类型Enum序列化DefaultSerializers$EnumSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15%E3%80%81EnumSet-%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96%EF%BC%9ADefaultSerializers-EnumSetSerializer"><span class="nav-number">12.</span> <span class="nav-text">15、EnumSet 类型序列化：DefaultSerializers$EnumSetSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#16%E3%80%81StringBuffer%E5%BA%8F%E5%88%97%E5%8C%96DefaultSerializers-StringBufferSerializer"><span class="nav-number">13.</span> <span class="nav-text">16、StringBuffer序列化DefaultSerializers$StringBufferSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#17%E3%80%81StringBuilder%E5%BA%8F%E5%88%97%E5%8C%96DefaultSerializers-StringBuilderSerializer"><span class="nav-number">14.</span> <span class="nav-text">17、StringBuilder序列化DefaultSerializers$StringBuilderSerializer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#18%E3%80%81TreeMap%E5%BA%8F%E5%88%97%E5%8C%96DefaultSerializers-TreeMapSerializer"><span class="nav-number">15.</span> <span class="nav-text">18、TreeMap序列化DefaultSerializers$TreeMapSerializer</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">中间件兴趣圈</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      
    </span>
  

  
    <span class="site-pv">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  
  <script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
  <script>AV.initialize("NNEhOL0iOcflg8f1U3HUqiCq-gzGzoHsz", "7kSmkbbb3DktmHALlShDsBUF");</script>
  <script>
    function showTime(Counter) {
      var query = new AV.Query(Counter);
      var entries = [];
      var $visitors = $(".leancloud_visitors");

      $visitors.each(function () {
        entries.push( $(this).attr("id").trim() );
      });

      query.containedIn('url', entries);
      query.find()
        .done(function (results) {
          var COUNT_CONTAINER_REF = '.leancloud-visitors-count';

          if (results.length === 0) {
            $visitors.find(COUNT_CONTAINER_REF).text(0);
            return;
          }

          for (var i = 0; i < results.length; i++) {
            var item = results[i];
            var url = item.get('url');
            var time = item.get('time');
            var element = document.getElementById(url);

            $(element).find(COUNT_CONTAINER_REF).text(time);
          }
          for(var i = 0; i < entries.length; i++) {
            var url = entries[i];
            var element = document.getElementById(url);
            var countSpan = $(element).find(COUNT_CONTAINER_REF);
            if( countSpan.text() == '') {
              countSpan.text(0);
            }
          }
        })
        .fail(function (object, error) {
          console.log("Error: " + error.code + " " + error.message);
        });
    }

    function addCount(Counter) {
      var $visitors = $(".leancloud_visitors");
      var url = $visitors.attr('id').trim();
      var title = $visitors.attr('data-flag-title').trim();
      var query = new AV.Query(Counter);

      query.equalTo("url", url);
      query.find({
        success: function(results) {
          if (results.length > 0) {
            var counter = results[0];
            counter.fetchWhenSave(true);
            counter.increment("time");
            counter.save(null, {
              success: function(counter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(counter.get('time'));
              },
              error: function(counter, error) {
                console.log('Failed to save Visitor num, with error message: ' + error.message);
              }
            });
          } else {
            var newcounter = new Counter();
            /* Set ACL */
            var acl = new AV.ACL();
            acl.setPublicReadAccess(true);
            acl.setPublicWriteAccess(true);
            newcounter.setACL(acl);
            /* End Set ACL */
            newcounter.set("title", title);
            newcounter.set("url", url);
            newcounter.set("time", 1);
            newcounter.save(null, {
              success: function(newcounter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(newcounter.get('time'));
              },
              error: function(newcounter, error) {
                console.log('Failed to create');
              }
            });
          }
        },
        error: function(error) {
          console.log('Error:' + error.code + " " + error.message);
        }
      });
    }

    $(function() {
      var Counter = AV.Object.extend("Counter");
      if ($('.leancloud_visitors').length == 1) {
        addCount(Counter);
      } else if ($('.post-title-link').length > 1) {
        showTime(Counter);
      }
    });
  </script>



  

  

  
  

  

  

  

</body>
</html>
